/*
Copyright (c) 2010 Andi Rustandi Djunaedi andird@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
 */
package com.appspot.mobileirisnextbus.client;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.appspot.mobileirisnextbus.shared.Constants;
import com.appspot.mobileirisnextbus.shared.Util;
import com.appspot.mobileirisnextbus.shared.data.Bus;
import com.appspot.mobileirisnextbus.shared.data.ButtonBus;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class MobileIrisNextBus implements EntryPoint {

	/**
	 * Create a remote service proxy to talk to the server-side Greeting
	 * service.
	 */
	private final BusServiceAsync busService = GWT.create(BusService.class);
	private final Label serverResponseLabel = new Label();
	private final HorizontalPanel linkPanel = new HorizontalPanel();
	private final HorizontalPanel inputPanel = new HorizontalPanel();
	private final HorizontalPanel buttonPanel = new HorizontalPanel();
	private final Label busStopLabel = new Label("BusStop");
	private final TextBox busStopField = new TextBox();
	private final Button sendButton = new Button("Submit");
	private final Label dateLabel = new Label(new Date().toString());
	private final Anchor srcLink = new Anchor(Constants.msgSrc,
			Constants.codeGoogleLink, Constants._blank);
	private final Anchor disclaimerLink = new Anchor(Constants.msgDisclaimer,
			Constants.disclaimerLink, Constants._blank);
	private final FlexTable resultTable = new FlexTable();

	// everthing will be put in main panel
	final VerticalPanel mainPanel = new VerticalPanel();

	/**
	 * Send the name from the nameField to the server and wait for a response.
	 */
	private final void getBusList() {
		// update date label
		this.dateLabel.setText(new Date().toString());

		// First, we validate the input.
		final String textToServer = this.busStopField.getText();
		if (!Util.isInputValid(textToServer)) {
			// if not number, do nothing
			return;
		}

		// disable btn and remove err msg
		this.sendButton.setEnabled(false);
		this.busStopField.setEnabled(false);
		this.serverResponseLabel.setText(null);

		// reset table
		this.resultTable.removeAllRows();
		this.setupResultTableHeader();

		// Then, we send the input to the server.
		this.busService.getBuses(textToServer, new AsyncCallback<List<Bus>>() {
			@Override
			public void onFailure(final Throwable caught) {
				MobileIrisNextBus.this.refreshDisplayForFailure(caught);
			}

			@Override
			public void onSuccess(final List<Bus> svcList) {
				MobileIrisNextBus.this.refreshDisplayForSuccess(svcList);
			}
		});
	}

	private final void getBusTimes(final String busNum, final int rowNum) {
		// clear time
		for (int i = 1; i < 3; i++) {
			final String str = MobileIrisNextBus.this.resultTable.getText(
					rowNum, i);
			if ((str == null) || !str.contains(".")) {
				MobileIrisNextBus.this.resultTable.setText(rowNum, i,
						Constants.msgRetry);
			}
		}
		this.busService.getBusTime(this.busStopField.getValue(), busNum,
				new AsyncCallback<List<String>>() {
					@Override
					public void onFailure(final Throwable caught) {
						// update time
						for (int i = 1; i < 3; i++) {
							String str = MobileIrisNextBus.this.resultTable
									.getText(rowNum, i);
							str += ".";
							MobileIrisNextBus.this.resultTable.setText(rowNum,
									i, str);
						}

						// keep trying until successful
						MobileIrisNextBus.this.getBusTimes(busNum, rowNum);
					}

					@Override
					public void onSuccess(final List<String> svcList) {
						MobileIrisNextBus.this.refreshDisplayForSuccess(rowNum,
								svcList);
					}
				});
	}

	/**
	 * This is the entry point method.
	 */
	@Override
	public void onModuleLoad() {
		// Create a handler for the sendButton and nameField
		final class InputHandler implements ClickHandler, KeyUpHandler,
				ChangeHandler {
			@Override
			public void onChange(final ChangeEvent event) {
				MobileIrisNextBus.this.getBusList();
			}

			/**
			 * Fired when the user clicks on the sendButton.
			 */
			@Override
			public void onClick(final ClickEvent event) {
				MobileIrisNextBus.this.getBusList();
				// Focus the cursor on the name field after submit
				MobileIrisNextBus.this.busStopField.setFocus(true);
				MobileIrisNextBus.this.busStopField.selectAll();
			}

			/**
			 * Fired when the user types in the nameField.
			 */
			@Override
			public void onKeyUp(final KeyUpEvent event) {
				if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
					MobileIrisNextBus.this.getBusList();
				}
			}

		}

		// Add a handler to send the name to the server
		final InputHandler handler = new InputHandler();
		this.sendButton.addClickHandler(handler);
		this.busStopField.addChangeHandler(handler);
		this.busStopField.addKeyUpHandler(handler);

		// add to root panel
		RootPanel.get("container").add(this.mainPanel);

		// setup main panel
		this.mainPanel.add(this.serverResponseLabel);
		this.mainPanel.add(this.linkPanel);
		this.mainPanel.add(this.inputPanel);
		this.mainPanel.add(this.buttonPanel);
		this.mainPanel.add(this.resultTable);

		// setup link panel
		this.linkPanel.add(this.srcLink);
		this.linkPanel.add(this.disclaimerLink);

		// setup input panel
		this.inputPanel.add(this.busStopField);
		this.inputPanel.add(this.busStopLabel);

		// setup button panel
		this.buttonPanel.add(this.sendButton);
		this.buttonPanel.add(this.dateLabel);

		// Focus the cursor on the name field when the app loads
		this.busStopField.setFocus(true);
		this.busStopField.selectAll();

		// set result table header
		this.setupResultTableHeader();

	}

	private final void refreshDisplayForFailure(final Throwable caught) {
		// Show the RPC error message to the user
		this.serverResponseLabel.addStyleName("serverResponseLabelError");
		this.serverResponseLabel.setText(Constants.SERVER_ERROR);

		// enable button
		this.sendButton.setEnabled(true);
		this.busStopField.setEnabled(true);
	}

	private final void refreshDisplayForSuccess(final int rowNum,
			final List<String> times) {
		// clear error msg
		this.serverResponseLabel.removeStyleName("serverResponseLabelError");
		this.serverResponseLabel.setText(null);

		int colNum = 1;
		for (final String time : times) {
			this.resultTable.setText(rowNum, colNum, time);
			colNum++;
		}

		// enable button
		this.sendButton.setEnabled(true);
		this.busStopField.setEnabled(true);
	}

	private final void refreshDisplayForSuccess(final List<Bus> bs) {
		// clear error msg
		this.serverResponseLabel.removeStyleName("serverResponseLabelError");
		this.serverResponseLabel.setText(null);

		final List<Bus> buses = bs;
		final List<ButtonBus> btns = new ArrayList<ButtonBus>(bs.size());

		// display all buses
		int rowNum = 1;
		for (final Bus bus : buses) {
			final ButtonBus btn = new ButtonBus(bus.getBusNum(), rowNum);
			this.resultTable.setWidget(rowNum, 0, btn);
			this.resultTable.setText(rowNum, 1, null);
			this.resultTable.setText(rowNum, 2, null);
			this.getBusTimes(bus.getBusNum(), rowNum);
			btns.add(btn);
			rowNum++;
		}

		// enable button
		this.sendButton.setEnabled(true);
		this.busStopField.setEnabled(true);

		// add click handler to btns
		for (final ButtonBus btn : btns) {
			btn.addClickHandler(new ClickHandler() {

				@Override
				public void onClick(final ClickEvent event) {
					MobileIrisNextBus.this.getBusTimes(btn.getText(),
							btn.getRowNum());
				}
			});
		}
	}

	private void setupResultTableHeader() {
		// set result table header
		this.resultTable.setText(0, 0, Constants.msgBus);
		this.resultTable.setText(0, 1, Constants.msgNext);
		this.resultTable.setText(0, 2, Constants.msgSubsequent);
	}

}
